<template>
    <div class="editorBody">
        <el-row class="myElRow1">
            <el-col :span="24">
                <div class="backBat-container">
                    <span @click="goBack()" class="backBtn"><< 返回</span>
                    <div style="margin-left: auto;margin-right: 20px;">
                        <el-popover placement="bottom-end" :width="400" trigger="click">
                            <template #reference>
                                <el-button>查看封面</el-button>
                            </template>
                                <el-upload ref="frontCoverUploadRef" class="avatar-uploader uploadBox" :action="path.baseUrl + '/common/upload'" list-type="picture"
                                    :auto-upload="false" :show-file-list="false" :data="CoverUploadData" :headers="CoverUploadHeaders"
                                    :on-success="handleAvatarSuccess" :on-change="handleChange" :before-upload="beforeAvatarUpload"
                                >
                                    <img v-if="news_updateDTO.frontCover" :src="news_updateDTO.frontCover" alt="封面图" class="avatar"/>
                                    <el-icon v-else class="avatar-uploader-icon">
                                        <el-icon><Plus /></el-icon>
                                    </el-icon>
                                </el-upload>
                            
                            <p style="text-align: center;color: rgb(140, 140, 140)">点击更换封面</p>
                        </el-popover>
                        <el-button @click="submitUpdate()" type="primary">保存修改</el-button>
                    </div>
                </div>
            </el-col>
        </el-row>

        <el-row class="myElRow2">
            <el-col :span="24">
                <div class="editorBar">
                    <Toolbar style="width: 1350px;"
                        :editor="editorRef"
                        :defaultConfig="toolbarConfig"
                    />
                </div>
            </el-col>
        </el-row>

        <div class="content">
            <div class="editor-container" style="border: 1px solid #ccc;height: 100%;">
                <div class="title-container">
                    <input v-model="news_updateDTO.title" placeholder="请输入标题......"/>
                </div>
                <div class="editor-text-area">
                    <Editor v-model="valueHtml"
                        :defaultConfig="editorConfig"
                        @onCreated="handleCreated"
                    />
                </div>
                
            </div>
        </div>

    </div>

</template>

<script lang="ts" setup>
import { onBeforeUnmount, ref, Ref, inject, shallowRef, onMounted, watch, watchEffect,nextTick } from 'vue'
import type { UploadProps,UploadInstance } from 'element-plus'
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
import { DomEditor, IDomEditor, IEditorConfig, IToolbarConfig } from '@wangeditor/editor'
import api from '@/api';
import path from "@/api/path.js";
import { SlateElement } from '@wangeditor/editor'
import { useRouter,useRoute } from 'vue-router'
import { NewsStore } from '@/stores/newsInfoStore';
import { TipStore } from '@/stores/tipStore';


const disableCacheFor = inject('disableCacheFor') as (componentNameList: string[]) => void;
const router = useRouter();
const newsStoreObject = NewsStore() as any;
const TipStoreObject = TipStore();
const news_updateDTO = ref({
    id: null,
    title: null,
    frontCover: null,
    content: null,
    categoryId: null,
    authorId: null,
    status: null,
})

const jwtToken = localStorage.getItem('wx_access-client'); //1.在本地存储中取出jwt令牌（由登录操作存储于本地）

// 拷贝属性函数
function copyProperties(source, target) {
  for (const key in source) {
    if (source.hasOwnProperty(key)) { // 确保是源对象自身的属性
      if (target.hasOwnProperty(key)) { // 检查目标对象是否有该属性
        target[key] = source[key]; // 拷贝属性值
      }
    }
  }
}
function goBack(){
    router.push('/Works');
}


//#region 封面上传相关
const frontCoverUploadRef = ref<UploadInstance>()
const CoverUploadHeaders = ref({ // 上传时附带的请求头  
  token: ''
})
const CoverUploadData = ref({ // 上传时附带的额外参数
  fullPath: 'img/client/',
})
const oldFrontCover = ref(null) //原先的封面地址
const isHasNewFrontCover = ref(false) //是否上传了新的的封面
const isUploadOver = ref(false) // 是否上传完毕
const handleAvatarSuccess: UploadProps['onSuccess'] = (response,uploadFile) => {
    news_updateDTO.value.frontCover = response.data
    //把旧的封面给删掉
    if(oldFrontCover.value != null){
        api.deleteObject([oldFrontCover.value])
    }
    isUploadOver.value = true
}

const beforeAvatarUpload: UploadProps['beforeUpload'] = (rawFile) => {
    if (!rawFile.type.startsWith('image/')) {
        TipStoreObject.errorTip('封面必须为图片格式!')
        return false
    } else if (rawFile.size / 1024 / 1024 > 2) {
        TipStoreObject.errorTip('封面大小不能超过 2MB!')
        return false
    }
    return true
}

const handleChange = (file, fileList)=>{
    if(isUploadOver.value == true){ // 如果已经上传完毕，则不执行任何操作(因为文件上传也会触发change事件，不要又把图片地址覆盖掉了)
        return
    }
    news_updateDTO.value.frontCover = URL.createObjectURL(file.raw) // 先实现本地预览，未真正上传
    isHasNewFrontCover.value = true
}
//#endregion


const imgSrcList = ref([])
const imgListObj = ref([])
const imgWhichHadDel = ref([])

const videoSrcList = ref([])
const videoListObj = ref([])
const videoWhichHadDel = ref([])

//删除富文本编辑器中已经上传到服务器，但在编辑阶段就被删除的文件
const DeleteFileWhichHadUploadFromEditor = async() => {
  if(editorRef.value){
      imgListObj.value = getImgMethod() //拿到当前富文本编辑器中的图片对象
      console.log("富文本编辑器中的图片对象为：",imgListObj.value)
      imgSrcList.value = imgListObj.value.map(e => e.src); //拿到图片对象们的src属性
      imgWhichHadDel.value = imageList.value.filter(item => !imgSrcList.value.includes(item)); //找出在编辑阶段就被删除的图片的完整路径
      
      videoListObj.value = getVideoMethod()
      console.log("富文本编辑器中的视频对象为：",videoListObj.value)
      videoSrcList.value = videoListObj.value.map(e => e.src); //拿到视频对象们的src属性
      videoWhichHadDel.value = videoList.value.filter(item => !videoSrcList.value.includes(item)); //找出在编辑阶段就被删除的视频的完整路径

      //调用接口删除这些文件
      if(imgWhichHadDel.value.length != 0){
        console.log("要删除的图片对象为：",imgWhichHadDel.value)
        await api.deleteObject(imgWhichHadDel.value)
      }
      if(videoWhichHadDel.value.length != 0){
        console.log("要删除的视频对象为：",videoWhichHadDel.value)
        await api.deleteObject(videoWhichHadDel.value)
      }
  }
}

// 提交修改
const submitUpdate = async()=>{
    // 1、确保信息完整
    if(!news_updateDTO.value.title || !news_updateDTO.value.content || !news_updateDTO.value.categoryId){
        alert("必要信息请填写完整")
        return
    }

    // 2、等待封面上传完毕后再执行下面的步骤
    if(isHasNewFrontCover.value == true){
        await new Promise((resolve, reject) => {
            frontCoverUploadRef.value!.submit(); //手动上传

            // 定义一个定时器，用于检查是否上传完毕
            const checkInterval = setInterval(() => {
                if (isUploadOver.value == true) { // 上传成功
                    clearInterval(checkInterval); // 清除定时器
                    isUploadOver.value = false;
                    resolve(news_updateDTO.value.frontCover); // resolve 表示上传成功
                }
            }, 100); // 每隔 100 毫秒检查一次

            // 设置一个超时时间，防止无限等待
            setTimeout(() => {
                clearInterval(checkInterval); // 清除定时器
                reject(new Error('图片上传超时')); // 超时后 reject
                return;
            }, 10000); // 超时时间为 10000 毫秒
        });
    }

    // 3、如果是被撤回的新闻，修改完毕发送到后端要变为待审核
    if(news_updateDTO.value.status == 2){
        news_updateDTO.value.status = 0 
    }
    
    // 4、修改新闻
    await api.updateWorks(news_updateDTO.value).then((res)=>{
        if(res.data.code == 1){
            TipStoreObject.successTip("修改新闻成功！")
        }else{
            TipStoreObject.errorTip("修改新闻失败！请稍后再试")
        }
        
    }).catch((err)=>{
        TipStoreObject.errorTip("修改新闻出错！请稍后再试")
    })

    // 5、在云存储中删除富文本编辑器中已经上传到服务器，但在编辑阶段就被删除的文件
    await DeleteFileWhichHadUploadFromEditor()


    // 6、删除本次存入临时文件标记表的信息
    if(imgSrcList.value.length != 0){
        await api.deleteTempFile(imageList.value) //删除本次存入临时文件标记表的信息（图片）
    }
    if(videoSrcList.value.length != 0){
        await api.deleteTempFile(videoList.value) //删除本次存入临时文件标记表的信息（视频）
    }
    disableCacheFor(['Profile','Works']) //刷新组件的挂载，因为数据更新了
    goBack()
}





//#region WangEditor相关代码------

type ImageElement = SlateElement & {
    src: string
    alt: string
    url: string
    href: string
}
type VideoElement = SlateElement & {
    src: string
    poster?: string
}
type InsertFnType_pic = (url: string, alt: string, href: string) => void
type InsertFnType_video = (url: string, poster: string ) => void


const editorRef = shallowRef() // 编辑器实例，必须用 shallowRef
const valueHtml = ref() // 内容 HTML
//编辑器初始化时触发
const handleCreated = (editor) => {
    editorRef.value = editor // 记录 editor 实例，重要！
}

//#region 1、关于修改【工具栏】配置
const toolbarConfig = {} as any
toolbarConfig.excludeKeys = [
  'fullScreen', // 如果要排除菜单组的话，写菜单组 key 的值即可
]

//#endregion


//#region 2、关于修改【编辑区】配置
  // 1、初始化 MENU_CONF 属性
  const editorConfig: Partial<IEditorConfig> = {  // JS 语法: const editorConfig = {   
      MENU_CONF: {}
  }

  // 2、配置开始
  editorConfig.placeholder = '请输入内容...'
  editorConfig.autoFocus = false // 是否自动聚焦到编辑区域

  //2.1.1关于插入图片的配置
  const imageList = ref([]) //已经插入或上传的图片
  editorConfig.MENU_CONF['insertImage'] = {
      //插入图片之后的回调
      onInsertedImage(imageNode: ImageElement | null) {
          if (imageNode == null) return
          const { src, alt, url, href } = imageNode
          imageList.value.push(src)
      },
  }

  // 2.1.2关于上传图片的配置！！！
  editorConfig.MENU_CONF['uploadImage'] = {
      server: path.baseUrl + '/common/upload', //后端接口地址
      fieldName: 'file', //后端自己规定的文件名字

      // 自定义上传参数，例如传递验证的 token 等。参数会被添加到 formData 中，一起上传到服务端。
      meta: {
        fullPath: 'img/client/', //图片上传到哪一个文件夹，后端自己规定的
      },
      // 自定义增加 http  header
      headers: {
          token: jwtToken,
      },

      maxFileSize: 2 * 1024 * 1024, //单个文件的最大体积限制，不设置的话默认就为 2M


    // 自定义插入图片（后端返回的格式如果不符合官方文档的规范，就这样做）
    customInsert(res: any, insertFn: InsertFnType_pic) {  
        let url =  res.data // res 即服务端的返回结果
        insertFn(url, "图片不见了", null) // 从 res 中找到 url(图片 src ，必须) alt(图片描述文字，非必须) href(图片的链接，非必须) ，然后插入图片
    },

    // 上传之前触发
    onBeforeUpload(file: File) { 
       
    },

    // 单个文件上传成功之后
    onSuccess(file: File, res: any) {
        console.log(`${file.name} 上传成功`, res)
        api.addTempFile(res.data) //将文件路径存入后端的临时文件表里
    },

    // 单个文件上传失败
    onFailed(file: File, res: any) {
        console.log(`${file.name} 上传失败`, res)
    },

    // 上传错误，或者触发 timeout 超时
    onError(file: File, err: any, res: any) {
        console.log(`${file.name} 上传出错`, err, res)
    },


    //【注意】不需要修改的不用写，wangEditor 会去 merge 当前其他配置
  }


  //2.2.1关于插入视频的配置
  const videoList = ref([]) //已经插入或上传的视频
  editorConfig.MENU_CONF['insertVideo'] = {
    //插入视频之后的回调
    onInsertedVideo(videoNode: VideoElement | null) {
      if (videoNode == null) return
        const { src } = videoNode
        videoList.value.push(src) //已经上传到服务器的视频的地址数组
    },
  }

  //2.2.2关于上传视频的配置！！！
  editorConfig.MENU_CONF['uploadVideo'] = {
    server: path.baseUrl + '/common/upload', //后端接口地址
    // form-data fieldName ，默认值 'wangeditor-uploaded-video'
    fieldName: 'file', //要改成后端规定的名字

    // 单个文件的最大体积限制，默认为 10M
    maxFileSize: 5 * 1024 * 1024, // 5M

    // 最多可上传几个文件，默认为 5
    maxNumberOfFiles: 3,

    // 选择文件时的类型限制，默认为 ['video/*'] 。如不想限制，则设置为 []
    allowedFileTypes: ['video/*'],

    // 自定义上传参数，例如传递验证的 token 等。参数会被添加到 formData 中，一起上传到服务端。
    meta: {
        fullPath: 'video/client/', //文件上传到哪一个文件夹，后端自己规定的
    },
    // 自定义增加 http  header
    headers: {
        token: jwtToken,
    },

    // 将 meta 拼接到 url 参数中，默认 false
    metaWithUrl: false,

    // 超时时间，默认为 30 秒
    timeout: 15 * 1000, // 15 秒

    // 视频不支持 base64 格式插入


    // 自定义插入视频
    customInsert(res: any, insertFn: InsertFnType_video) {
      let url =  res.data // res 即服务端的返回结果
      insertFn(url, null) // poster是视频封面图片 url ，可选
    },


    // 单个文件上传成功之后
    onSuccess(file: File, res: any) {  // TS 语法
        console.log(`${file.name} 上传成功`, res)
    },

    // 单个文件上传失败
    onFailed(file: File, res: any) {  // TS 语法
        console.log(`${file.name} 上传失败`, res)
    },

    // 上传错误，或者触发 timeout 超时
    onError(file: File, err: any, res: any) {  // TS 语法
        console.log(`${file.name} 上传出错`, err, res)
    },
}
//#endregion


//#region 关于组件间传值
const model = defineModel()

//监听编辑器内部的内容变化，同步给外部
watch(() => valueHtml.value, (newValue, oldValue) => {
    model.value = newValue
})
//监听外部的model值，同步给内部
watch(() => model.value, (newValue, oldValue) => {
    if(newValue == null){ newValue = '' } // valueHtml.value为null时会报错，不能为null
    valueHtml.value = newValue
})

//获取当前编辑器的所有图片
const getImgMethod = ()=>{
    return editorRef.value.getElemsByType('image')
}
//获取当前编辑器的所有视频
const getVideoMethod = ()=>{
    return editorRef.value.getElemsByType('video')
}


// 暴露给父组件的方法或属性
defineExpose({imageList,videoList,getImgMethod,getVideoMethod}) 


//#endregion


//#endregion WangEditor相关代码结束------




//监听编辑器内部的内容变化，同步给外部
watch(() => valueHtml.value, (newValue, oldValue) => {
    news_updateDTO.value!.content = newValue
})


onMounted(async() => {
    //console.log("获得的新闻信息为：",newsStoreObject.newsInfo)
    CoverUploadHeaders.value.token = localStorage.getItem('wx_access-client') //设置token
    oldFrontCover.value = newsStoreObject.newsInfo.frontCover
    copyProperties(newsStoreObject.newsInfo,news_updateDTO.value)

    watchEffect(() => { // 确保编辑器实例已初始化再执行
        if (editorRef.value) {
            valueHtml.value = newsStoreObject.newsInfo.content
            nextTick(() => {
                const OriginalImgList = getImgMethod(); // 富文本里本来就有的图片
                if (OriginalImgList.length > 0) {
                    OriginalImgList.forEach((item: any) => {
                        imageList.value.push(item.src); // 初始化已经插入或上传的图片
                    });
                }
                const OriginalVideoList = getVideoMethod();
                if (OriginalVideoList.length > 0) {
                    OriginalVideoList.forEach((item: any) => {
                        videoList.value.push(item.src);
                    });
                }
            });
        }
    });
})

// 组件销毁时，也及时销毁编辑器
onBeforeUnmount(() => {
    const editor = editorRef.value
    if (editor == null) return
    editor.destroy()
})

</script>


<style lang="scss" scoped>
.editorBody{
    background-color: #F5F5F5;
    overflow-y: hidden;
}

.backBtn:hover{
    cursor: pointer;
    color: #409EFF;
}

.myElRow1{
    position: fixed;
    width: 100%;
    top: 0;
    z-index: 1000;
}
.myElRow2{
    position: fixed;
    width: 100%;
    top: 36px;
    z-index: 1000;
    height: auto; /* 根据内容自动调整高度 */ 
}
.myElRow2:focus{
    overflow: hidden;
}

.backBat-container {
  padding-left: 30px;
  min-width: 1320px;
  border-radius: 4px;
  min-height: 36px;
  display: flex;
  align-items: center;
  background-color: #ffffff;
}
.uploadBox{
    display: flex;
    justify-content: center;
    margin-bottom: 10px;
}
.avatar-uploader .avatar {
    width: 350px;
    height: 262px;
    display: block;
    object-fit: cover
}

.editorBar{
    display: flex;
    justify-content: center;
    align-items: center;
    background-color: #ffffff;
    border-top: 1px solid #ccc;
    border-bottom: 1px solid #ccc;
    min-width: 1350px;height: 100%;
}

.content{
    height: calc(100% - 40px);
    position: relative;
    z-index: 0;
    // margin-top: 35px;
    margin-top: 120px;
}
.editor-container{
    width: 850px;
    margin: 30px auto 150px auto;
    background-color: #fff;
    padding: 20px 50px 50px 50px;
    border: 1px solid #e8e8e8;
    box-shadow: 0 2px 10px rgb(0 0 0 / 12%);
}
.title-container {
    padding: 20px 0;
    border-bottom: 1px solid #e8e8e8;
}
.title-container input {
    font-size: 30px;
    border: 0;
    outline: none;
    width: 100%;
    line-height: 1;
}
.editor-text-area {
    min-height: 900px;
    margin-top: 20px;
}



/* 确保顶部条能被操作 */
@media (min-width: 700px) and (max-width: 1350px) {
  .myElRow1 {
    position: fixed;
    top: 0;
    z-index: 1000;
    overflow: auto; /* 添加滚动条 */
  }
  .myElRow2 {
    position: fixed;
    top: 36px;
    z-index: 1000;
    //overflow-x:auto; /* 添加滚动条 */
    //overflow-y: auto;
  }
  .backBat-container{
    min-width: 300px;
  }
  .editorBar{
    min-width: 700px;
  }
}

@media (max-width: 700px ) {
  .backBat-container{
    min-width: 300px;
  }
  .editorBar{
    min-width: 300px;
  }
  .content{
    margin-top: 160px;
  }
}

@media (max-width: 345px ) {
  .backBat-container{
    min-width: 300px;
  }
  .editorBar{
    min-width: 300px;
  }
  .content{
    margin-top: 240px;
  }
}
</style>